package cn.zwx.member.svc.service.impl;

import cn.zwx.member.svc.domain.bo.UserPointsBO;
import cn.zwx.member.svc.domain.constans.MemberConstans;
import cn.zwx.member.svc.domain.entity.PointsDetailEntity;
import cn.zwx.member.svc.domain.entity.SignRewardEntity;
import cn.zwx.member.svc.domain.entity.UserLevelEntity;
import cn.zwx.member.svc.domain.entity.UserPointsEntity;
import cn.zwx.member.svc.domain.enums.UserLevelEnum;
import cn.zwx.member.svc.mapper.PointsDetailMapper;
import cn.zwx.member.svc.mapper.UserLevelMapper;
import cn.zwx.member.svc.mapper.UserPointsMapper;
import cn.zwx.member.svc.service.UserPointsService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author zhangwenxue
 */
@Service
public class UserPointsServiceImpl implements UserPointsService {

    @Resource
    UserPointsMapper userPointsMapper;
    @Resource
    PointsDetailMapper pointsDetailMapper;

    @Resource
    UserLevelMapper userLevelMapper;

    @Override
    public void addUserPoint(UserPointsBO userPointsBO) {
        // 1.查询用户集合
        LambdaQueryWrapper<UserPointsEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPointsEntity::getUserId,userPointsBO.getUserId());
        UserPointsEntity userPointsEntity = userPointsMapper.selectOne(queryWrapper);
        // 2.如果不存在则插入
        final List<UserPointsBO.UserPointsDetailBO> detailList = userPointsBO.getDetailList();
        final Long totalReward = detailList.stream().mapToLong(UserPointsBO.UserPointsDetailBO::getPointsValue).sum();
        // 3.插入用户积分并且记录等级
        if (ObjectUtils.isEmpty(userPointsEntity)){
            this.processNonRecord(userPointsBO, detailList, totalReward);
            return;
        }
        this.processExistRecord(userPointsEntity, detailList, totalReward);
    }

    @Override
    public UserPointsEntity selectByUserId(String userId) {
        LambdaQueryWrapper<UserPointsEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPointsEntity::getUserId,userId);
        final UserPointsEntity userPointsEntity = userPointsMapper.selectOne(queryWrapper);
        if (!ObjectUtils.isEmpty(userPointsEntity)){
            return userPointsEntity;
        }
        LambdaQueryWrapper<UserLevelEntity> levelWrapper = new LambdaQueryWrapper<>();
        levelWrapper.eq(UserLevelEntity::getLevel,UserLevelEnum.LEVEL_1.getCode());
        final UserLevelEntity userLevelEntity = userLevelMapper.selectOne(levelWrapper);
        UserPointsEntity defaultUserPoints = new UserPointsEntity();
        defaultUserPoints.setEmpiricalValue(userLevelEntity.getEmpiricalValue());
        defaultUserPoints.setUserId(userId);
        defaultUserPoints.setLevel(UserLevelEnum.LEVEL_1.getCode());
        defaultUserPoints.setNextLevelEmpirical(userLevelEntity.getEmpiricalValue());
        defaultUserPoints.setPoints(MemberConstans.LONG_0);
        defaultUserPoints.setUpdateTime(new Date());
        defaultUserPoints.setCreateTime(new Date());
        return defaultUserPoints;
    }

    private void processExistRecord(UserPointsEntity userPointsEntity, List<UserPointsBO.UserPointsDetailBO> detailList, Long totalReward) {
        final Integer level = userPointsEntity.getLevel();
        LambdaQueryWrapper<UserLevelEntity> userLevelWrapper = new LambdaQueryWrapper<>();
        userLevelWrapper.eq(UserLevelEntity::getLevel, level);
        final UserLevelEntity userLevelEntity = userLevelMapper.selectOne(userLevelWrapper);
        // 所在等级升级的经验值
        final Long empiricalValue = userLevelEntity.getEmpiricalValue();
        // 用户累计经验值
        Long sumEmpiricalValue = userPointsEntity.getEmpiricalValue() + totalReward;
        Long sumPoints = userPointsEntity.getPoints() + totalReward;
        long subEmpiricalValue = empiricalValue - sumEmpiricalValue;
        if (subEmpiricalValue < 0){
            // 需要升级
            Integer currentLevel = Math.addExact(level,1)== UserLevelEnum.LEVEL_6.getCode() ? UserLevelEnum.LEVEL_6.getCode()
                    :Math.addExact(level,1);
            userPointsEntity.setPoints(sumPoints);
            if (UserLevelEnum.LEVEL_6.getCode() != userLevelEntity.getLevel()){
                LambdaQueryWrapper<UserLevelEntity> levelWrapper = new LambdaQueryWrapper<>();
                levelWrapper.eq(UserLevelEntity::getLevel, currentLevel);
                final UserLevelEntity nextUserLevel = userLevelMapper.selectOne(levelWrapper);
                userPointsEntity.setNextLevelEmpirical(nextUserLevel.getEmpiricalValue() - subEmpiricalValue);
                userPointsEntity.setEmpiricalValue(sumEmpiricalValue);
            }else if (sumEmpiricalValue >= userLevelEntity.getEmpiricalValue()) {
                userPointsEntity.setPoints(sumPoints);
                userPointsEntity.setEmpiricalValue(userLevelEntity.getEmpiricalValue());
                userPointsEntity.setNextLevelEmpirical(MemberConstans.LONG_0);
            }
            userPointsEntity.setLevel(currentLevel);
        }else {
            // 不需要升级
            userPointsEntity.setPoints(totalReward);
            userPointsEntity.setEmpiricalValue(sumEmpiricalValue);
            userPointsEntity.setNextLevelEmpirical(subEmpiricalValue);
        }
        userPointsEntity.setUpdateTime(new Date());
        userPointsMapper.updateByPrimaryKeySelective(userPointsEntity);
        this.batchInsertDetail(detailList, userPointsEntity.getId());
    }

    private void processNonRecord(UserPointsBO userPointsBO, List<UserPointsBO.UserPointsDetailBO> detailList, Long totalReward) {
        LambdaQueryWrapper<UserLevelEntity> userLevelWrapper = new LambdaQueryWrapper<>();
        userLevelWrapper.eq(UserLevelEntity::getLevel, UserLevelEnum.LEVEL_1.getCode());
        final UserLevelEntity userLevelEntity = userLevelMapper.selectOne(userLevelWrapper);
        final Long empiricalValue = userLevelEntity.getEmpiricalValue();
        Long subValue = empiricalValue - totalReward;
        UserPointsEntity userPointsEntity = new UserPointsEntity();
        userPointsEntity.setUserId(userPointsBO.getUserId());
        userPointsEntity.setPoints(totalReward);
        userPointsEntity.setLevel(UserLevelEnum.LEVEL_1.getCode());
        userPointsEntity.setEmpiricalValue(totalReward);
        userPointsEntity.setCreateTime(new Date());
        userPointsEntity.setUpdateTime(new Date());
        userPointsEntity.setNextLevelEmpirical(subValue);
        userPointsMapper.insertSelective(userPointsEntity);
        this.batchInsertDetail(detailList, userPointsEntity.getId());
    }

    private void batchInsertDetail(List<UserPointsBO.UserPointsDetailBO> detailList, Long userPointsId) {
        final List<PointsDetailEntity> pointsDetailList = new ArrayList<>();
        for (UserPointsBO.UserPointsDetailBO userPointsDetailBO : detailList) {
            final PointsDetailEntity detailEntity = new PointsDetailEntity();
            detailEntity.setUserPointsId(userPointsId);
            detailEntity.setPointsValue(userPointsDetailBO.getPointsValue());
            detailEntity.setCreateTime(new Date());
            detailEntity.setPointsTypeDesc(userPointsDetailBO.getPointsTypeDesc());
            detailEntity.setPointsType(userPointsDetailBO.getPointsType());
            pointsDetailList.add(detailEntity);
        }
        pointsDetailMapper.batchInsert(pointsDetailList);
    }
}
